package cn.kailangtu.serialport;

import android.os.SystemClock;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import android_serialport_api.SerialPort;
import cn.kailangtu.serialportv2.DataAnalysis;
import cn.kailangtu.serialportv2.DataVersion2;
import cn.kailangtu.serialportv2.ReceiveMsgListenerV2;

public class SerialPortApi {

    private static String TAG = "SerialPortApi==>";
    private String portName;
    private int baudrate;
    private int flags;
    private LinkedBlockingQueue<String> reportMsgQueue = null;
    private LinkedBlockingQueue<String> sendMsgQueue = null;

    private  OutputStream os;
    private  InputStream is;
    private  SerialPort serial;
    private  int resultSize;

    private  byte[] rxByteArray;


    private ReceiveMsgListener listener;
    private static boolean isReading = false;
    private static boolean isProcess = false;
    private static boolean isSending = false;
    private static boolean isSpying = false;

    private long lastReceiveTime =0;
    private boolean isOpen = false;

    public SerialPortApi(){
        reportMsgQueue = new LinkedBlockingQueue<>(500);
        sendMsgQueue = new LinkedBlockingQueue<>(500);
    }


    /***
     * 打开串口
     * @param portName 串口名称，例：/dev/ttyS1
     * @param baudrate 波特率
     * @param flags flag
     * @return
     */
    public boolean openSerialPortV2(String portName,int baudrate,int flags,ReceiveMsgListenerV2 listener)
    {
        try {
            this.portName = portName;
            this.baudrate = baudrate;
            this.flags = flags;
            this.msgListener = listener;
            serial = new SerialPort(new File(portName), baudrate, flags);

            os = serial.getOutputStream();
            is = serial.getInputStream();

            lastReceiveTime = System.currentTimeMillis();
            isOpen = true;

            SpyThread spyThread = new SpyThread();
            spyThread.start();

            SendThread sendThread = new SendThread();
            sendThread.start();

            ReadThread readThread = new ReadThread();
            readThread.start();

            ProcessThread processThread = new ProcessThread();
            processThread.start();

            return true;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return false;
    }


    /***
     * 打开串口
     * @param portName 串口名称，例：/dev/ttyS1
     * @param baudrate 波特率
     * @param flags flag
     * @return
     */
    public boolean openSerialPort(String portName,int baudrate,int flags,ReceiveMsgListener listener)
    {
        try {
            this.portName = portName;
            this.baudrate = baudrate;
            this.flags = flags;
            this.listener = listener;
            serial = new SerialPort(new File(portName), baudrate, flags);

            os = serial.getOutputStream();
            is = serial.getInputStream();


            ReadThread readThread = new ReadThread();
            readThread.start();

            ProcessThread processThread = new ProcessThread();
            processThread.start();

            return true;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return false;
    }

    public void close(){
        serial.close();

    }


    /***
     * 发送数据
     * @param addressId
     * @param data
     * @param isStandardFrame
     */
    public void send(byte[] addressId,byte[] data,boolean isStandardFrame)
    {
        byte length = (byte)data.length;
        if(!isStandardFrame)
        {
            length+=0x80;
        }

        String cmd = "5A"
                + CommonUtils.ByteArrayToHexString(new byte[]{length})
                + CommonUtils.ByteArrayToHexString(addressId)+CommonUtils.ByteArrayToHexString(data);
        cmd += getValidate(cmd);
        cmd +="A5";


        Log.e(TAG,cmd);

        try {
           // Log.e(TAG,"send Data:"+cmd);
            os.write(CommonUtils.hexStringToByteArray(cmd));
            os.flush();
        } catch (IOException e) {
            //Log.e(TAG,"send IOException"+e.getMessage());
            e.printStackTrace();
            onListenerErr(e.getMessage());
        } catch(Exception e)
        {
            e.printStackTrace();
            onListenerErr(e.getMessage());
        }
    }


    private static String getValidate(String cmd)
    {
        byte[] cmdByte = CommonUtils.hexStringToByteArray(cmd);
        byte result = cmdByte[0];
        for(int i =1;i< cmdByte.length;i++){
            result = (byte)(result ^ cmdByte[i]);
        }

        return CommonUtils.ByteArrayToHexString(new byte[]{result});
    }


    private String getModeString(int mode)
    {
        String modeString = "05";
        switch(mode)
        {
            case 1:
                modeString = "01";
                break;
            case 2:
                modeString = "02";
                break;
            case 3:
                modeString = "03";
                break;
            case 4:
                modeString = "04";
                break;
            case 5:
                modeString = "05";
                break;
        }
        String cmd = "AT+FORWARD_MODE="+modeString+"\r\n";
        return cmd;
    }

    public void setBaudrate(String localBaudrate,String canBaudrate,Integer mode){
        String step1  = "+++AT\r\n";
        String step2 = "AT+COM_BAUD="+localBaudrate+"\r\n";
        String step3 = "AT+CAN_B_CODE="+canBaudrate+"\r\n";
        String step4 = getModeString(mode);
        String step5 = "AT+SAVE\r\n";
        String step6 = "AT+RESET\r\n";



        sendString(step1);
        SystemClock.sleep(100);
        sendString(step2);
        SystemClock.sleep(100);
        sendString(step3);
        SystemClock.sleep(100);
        sendString(step4);
        SystemClock.sleep(100);
        sendString(step5);
        SystemClock.sleep(100);
        sendString(step6);

    }
    private void sendString(String data)
    {
        String cmd = data;
        try {
           // Log.e(TAG,"send Data:"+cmd);
            os.write(cmd.getBytes());
            os.flush();
        } catch (IOException e) {
           // Log.e(TAG,"send IOException"+e.getMessage());
            e.printStackTrace();
            onListenerErr(e.getMessage());
        } catch(Exception e)
        {
            e.printStackTrace();
            onListenerErr(e.getMessage());
        }
    }

    public void sendBytes(byte[] data){
        try {
           // Log.d("SERIALAAAAA","SEND DATA:"+CommonUtils.ByteArrayToHexString(data));
            os.write(data);
            os.flush();
        } catch (IOException e) {
           // Log.e(TAG,"send IOException"+e.getMessage());
            e.printStackTrace();
            onListenerErr(e.getMessage());
        } catch(Exception e)
        {
            //Log.e(TAG,"send IOException"+e.getMessage());
            e.printStackTrace();
            onListenerErr(e.getMessage());
        }
    }


    private void reOpen(){

        try{

            close();
            os.close();
            is.close();
            os = null;
            is = null;
            SystemClock.sleep(500);

        }
        catch(Exception e){}
        openSerialPortV2(this.portName,this.baudrate,this.flags,this.msgListener);

    }



    ReceiveMsgListenerV2 msgListener;

    private class SpyThread extends Thread{
        @Override
        public void run(){
            super.run();
            if(!isSpying)
            {
                isSpying = true;
                while(!isInterrupted()){

                    if(isOpen &&  (System.currentTimeMillis() - lastReceiveTime) > 20000)
                    {
                        reOpen();
                    }
                    SystemClock.sleep(1000);
                }
            }
        }
    }

    private class SendThread extends Thread{
        @Override
        public void run(){
            super.run();
            if(!isSending){
                isSending = true;
                while(!isInterrupted()){
                    try {
                        String currentCmd = sendMsgQueue.take();
                        sendString(currentCmd);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                isSending = false;
            }
        }
    }

    private class ProcessThread extends  Thread{
        @Override
        public void run(){
            super.run();
            if(!isProcess) {
                isProcess = true;
                while(!isInterrupted()){

                    try {
                        String currentCmd = reportMsgQueue.take();
                        DataVersion2 dataVersion2 =  DataVersion2.build(currentCmd);
                        if(msgListener != null)
                        {
                            msgListener.onReceiveData(dataVersion2.getData());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                isProcess = false;
            }
        }
    }



    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            if(!isReading) {
                while (!isInterrupted()) {
                    isReading = true;
                    try {
                        byte[] buffer = new byte[64];
                        if (is == null) {
                            return;
                        }
                        SystemClock.sleep(1);
                        int tempSize;
                        tempSize = is.read(buffer);
                        if (tempSize > 0) {
                            onDataReceived(buffer, tempSize);
                        }
                    } catch (IOException e) {
                       // Log.e(TAG, "ReadThread3异常");
                        e.printStackTrace();
                        return;
                    }
                }
                isReading = false;
            }
        }
    }



    private  void onDataReceived(byte[] paramArrayOfByte,
                                       int paramInt) {
        rxByteArray = CommonUtils.ArrayAppend(rxByteArray, paramArrayOfByte, paramInt);
        if (rxByteArray == null) {
            return;
        }
        String byteStr = CommonUtils.ByteArrayToHexString(rxByteArray);
        //Log.e(TAG,"receive msg2:"+byteStr);
        resultSize += paramInt;


        if(isCompleteThree(rxByteArray))
        {
            resultSize = 0;
            rxByteArray = null;
        }

    }



    private boolean isComplete() {

        if(rxByteArray != null && rxByteArray.length>1
                && (rxByteArray[rxByteArray.length -2 ] & 0xFF) == 0x7D
                && (rxByteArray[rxByteArray.length - 1] & 0xFF) == 0x7D
        ) {
            List<String> lsCmd = DataAnalysis.GetCmd(CommonUtils.ByteArrayToHexString( rxByteArray));
            for(String cmd : lsCmd)
            {
                reportMsgQueue.offer(cmd);
            }
            return true;
        }
        return false;



    }

    private boolean isCompleteTest()
    {
        if(listener != null)
        {

            listener.errorMsg(CommonUtils.ByteArrayToHexString(rxByteArray));
        }




        return true;

    }

    String lastAddress = "";


    private boolean isCompleteThree(byte[] data)
    {
        if(data != null && data.length>=8)
        {
            String cmdString = CommonUtils.ByteArrayToHexString(data).toUpperCase();
            process(cmdString);
            return true;
        }
        return false;
    }

    private boolean isCompleteEx(byte[] data)
    {
        if(data != null && data.length>=8)
        {
            String cmdString = CommonUtils.ByteArrayToHexString(data).toUpperCase();
            if((data[1] & 0xFF) == 0x5A && (data[0] * 0xFF) != 0x5A )
            {
                cmdString = cmdString.substring(2);
                data = CommonUtils.hexStringToByteArray(cmdString);
            }

            if(cmdString.startsWith("5A") && cmdString.endsWith("A5"))
            {
                if(cmdString.indexOf("A55A")>-1)
                {
                    List<String> lsCmdAll = new ArrayList<>();
                    String cmdSource = cmdString;
                    int position = cmdString.indexOf("A55A");
                    while(position>-1)
                    {
                        System.out.println(cmdSource);
                        lsCmdAll.add(cmdSource.substring(0, position + 2));
                        cmdSource = cmdSource.substring(position + 2);
                        position = cmdSource.indexOf("A55A");
                    }
                    lsCmdAll.add(cmdSource);
                    for(int i = 0;i< lsCmdAll.size();i++){
                        byte[] dataCurr = CommonUtils.hexStringToByteArray(lsCmdAll.get(i));
                        if(validateData(dataCurr))
                        {
                            onListenerDataEx(ReceiveData.build(dataCurr));
                        }
                    }
                }
                else {
                    if (validateData(data)) {
                        // onListenerData( receiveDataFormat(data));
                        onListenerDataEx(ReceiveData.build(data));

                    } else {
                        onListenerErr("数据未校验成功：" + cmdString);
                    }
                }
                return true;

            }

            else
            {
                onListenerErr("开头结尾不对,丢掉："+cmdString);
            }
        }
        return true;
    }




    private boolean validateData(byte[] data)
    {
        byte validateData = data[data.length-2];
        int c = data[0];
        for(int i =1;i< data.length -2;i++)
        {
            c = c ^ data[i];
        }
        if((c & 0xFF) == (validateData& 0xff))
        {
            return true;
        }
        return false;
    }



    private byte[] receiveDataFormat(byte[] source)
    {
        int sourceLength = source.length;
        int resultLength = sourceLength -8;
        byte[] result = new byte[resultLength];
        for(int i = 0;i< resultLength;i++)
        {
            int sourceIndex = i+6;
            result[i] = source[sourceIndex];
        }

        return result;
    }

    private void onListenerData(byte[] data)
    {
        if(listener != null)
        {
            listener.portReceiveData( ReceiveData.build(data));
        }
    }

    private void onListenerDataEx(ReceiveData data)
    {
        if(listener != null){
            listener.portReceiveData(data);
        }
    }

    private void onListenerErr(String err)
    {
        if(listener != null)
        {
            listener.errorMsg(err);
        }
    }

    private  void process(String source)
    {
        String  cmdString = source.toUpperCase();
        while(cmdString.length()>1 && !cmdString.startsWith("5A"))
        {
            cmdString = cmdString.substring(2);
        }
        if(cmdString.length() < 2)
        {
            return ;
        }
        if(cmdString.startsWith("5A"))
        {
            if(cmdString.length()>2)
            {
                int length = getLen(cmdString.substring(3,4));
                int allLength =  4+8+length*2+2+2;
                if(cmdString.length()>=allLength)
                {
                    if(cmdString.substring(allLength-2,allLength).equals("A5"))
                    {
                        String findString = cmdString.substring(0,allLength);
                        cmdString = cmdString.substring(allLength);
                        onListenerDataEx(ReceiveData.build(CommonUtils.hexStringToByteArray( findString)));
                       // System.out.println("找到一条："+findString);
                    }
                    else
                    {
                        cmdString = cmdString.substring(2);
                    }
                }
                else
                {
                    cmdString = cmdString.substring(2);
                }
            }
            else
            {
                cmdString = cmdString.substring(2);
            }
        }
        process(cmdString);
    }

    private  int getLen(String hexChar)
    {
        hexChar = hexChar.toUpperCase();
        int result = 8;
        try{
            result = Integer.parseInt(hexChar);
        }
        catch(Exception e)
        {

        }
        return result;

    }
}
